package com.gitee.wsl.net.server.health


import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
import io.ktor.server.application.ApplicationCall
import io.ktor.server.application.createApplicationPlugin
import io.ktor.server.application.install
import io.ktor.server.application.pluginOrNull
import io.ktor.server.response.respondText
import io.ktor.server.routing.Route
import io.ktor.server.routing.Routing
import io.ktor.server.routing.get
import io.ktor.server.routing.route
import io.ktor.server.routing.routing
import kotlinx.serialization.json.Json

val KHealth = createApplicationPlugin(
    name = "KHealth",
    createConfiguration = ::KHealthConfiguration,
    body = {
        onCall { call ->
            KHealthPlugin(this.pluginConfig).apply { interceptor(call) }
        }
    }
)

class KHealthPlugin internal constructor(private val config: KHealthConfiguration) {

    /**
     * Interceptor that handles all http requests. If either the health check or ready endpoint are
     * called it will return a custom response with the result of each custom check if any are defined.
     */
    fun interceptor(call: ApplicationCall) {
//        val routingBlock: Routing.() -> Unit = {
//            val routing: Route.() -> Unit = {
//                if (config.readyCheckEnabled) route(config.readyCheckPath) {
//                    get {
//                        val (status, responseBody) = processChecks(
//                            checkLinkedList = config.readyChecks,
//                            passingStatusCode = config.successfulCheckStatusCode,
//                            failingStatusCode = config.unsuccessfulCheckStatusCode
//                        )
//                        this.call.respondText(responseBody, ContentType.Application.Json, status)
//                    }
//                }
//                if (config.healthCheckEnabled) route(config.healthCheckPath) {
//                    get {
//                        val (status, responseBody) = processChecks(
//                            checkLinkedList = config.healthChecks,
//                            passingStatusCode = config.successfulCheckStatusCode,
//                            failingStatusCode = config.unsuccessfulCheckStatusCode
//                        )
//                        this.call.respondText(responseBody, ContentType.Application.Json, status)
//                    }
//                }
//            }
//            config.wrapWith?.invoke(this, routing) ?: routing(this)
//        }
        //call.application.pluginOrNull(Routing)?.apply(routing) ?: call.application.install(Routing, routing)
        call.application.routing{
            val routing: Route.() -> Unit = {
                if (config.readyCheckEnabled) route(config.readyCheckPath) {
                    get {
                        val (status, responseBody) = processChecks(
                            checkLinkedList = config.readyChecks,
                            passingStatusCode = config.successfulCheckStatusCode,
                            failingStatusCode = config.unsuccessfulCheckStatusCode
                        )
                        this.call.respondText(responseBody, ContentType.Application.Json, status)
                    }
                }
                if (config.healthCheckEnabled) route(config.healthCheckPath) {
                    get {
                        val (status, responseBody) = processChecks(
                            checkLinkedList = config.healthChecks,
                            passingStatusCode = config.successfulCheckStatusCode,
                            failingStatusCode = config.unsuccessfulCheckStatusCode
                        )
                        this.call.respondText(responseBody, ContentType.Application.Json, status)
                    }
                }
            }
            config.wrapWith?.invoke(this, routing) ?: routing(this)
        }
    }

    /**
     * Process the checks for a specific endpoint returning the evaluation of each check as a JSON string
     * with a status code. If any of the checks are false, then a [HttpStatusCode.InternalServerError] will be returned,
     * otherwise a [HttpStatusCode.OK] will be returned.
     * @param checkLinkedList A linkedlist of [Check] to be run.
     * @return A pair including a [HttpStatusCode] and a JSON encoded string of each check with their result.
     */
    private suspend fun processChecks(
        checkLinkedList: LinkedHashSet<Check>,
        passingStatusCode: HttpStatusCode,
        failingStatusCode: HttpStatusCode
    ): Pair<HttpStatusCode, String> {
        val checksWithResults = checkLinkedList.associate { Pair(it.checkName, it.check.invoke()) }
        val status = if (checksWithResults.containsValue(false)) {
            failingStatusCode
        } else passingStatusCode
        return Pair(status, Json.encodeToString(checksWithResults))
    }
}



